#include "ChecksumCRC.h"

CRC32_t CRC32_ProcessSingleBuffer( const void *p, int len )
{
	CRC32_t crc;

	CRC32_Init( &crc );

	CRC32_ProcessBuffer( &crc, p, len );

	CRC32_Final( &crc );

	return crc;
}

void CRC32_Init(CRC32_t *pulCRC)
{
	*pulCRC = CRC32_INIT_VALUE;
}

void CRC32_Final(CRC32_t *pulCRC)
{
	*pulCRC ^= CRC32_XOR_VALUE;
}

CRC32_t	CRC32_GetTableEntry( unsigned int slot )
{
	return pulCRCTable[(unsigned char)slot];
}

void CRC32_ProcessBuffer(CRC32_t *pulCRC, const void *pBuffer, int nBuffer)
{
	CRC32_t ulCrc = *pulCRC;
	unsigned char *pb = (unsigned char *)pBuffer;

	unsigned int nFront;
	int nMain;

JustAfew:

	switch (nBuffer)
	{
	case 7:

		ulCrc  = pulCRCTable[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8);

	case 6:
		ulCrc  = pulCRCTable[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8);

	case 5:
		ulCrc  = pulCRCTable[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8);


	case 4:
		ulCrc ^= *(CRC32_t *)pb; // Warning, this only works on little-endian.
		ulCrc  = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8);

		ulCrc  = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8);
		ulCrc  = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8);

		ulCrc  = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8);
		*pulCRC = ulCrc;
		return;

	case 3:
		ulCrc  = pulCRCTable[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8);


	case 2:
		ulCrc  = pulCRCTable[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8);

	case 1:
		ulCrc  = pulCRCTable[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8);


	case 0:
		*pulCRC = ulCrc;
		return;
	}

	// We may need to do some alignment work up front, and at the end, so that
	// the main loop is aligned and only has to worry about 8 byte at a time.
	//
	// The low-order two bits of pb and nBuffer in total control the
	// upfront work.
	//
	nFront = ((unsigned int)pb) & 3;
	nBuffer -= nFront;
	switch (nFront)
	{
	case 3:
		ulCrc  = pulCRCTable[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8);

	case 2:
		ulCrc  = pulCRCTable[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8);
	case 1:
		ulCrc  = pulCRCTable[*pb++ ^ (unsigned char)ulCrc] ^ (ulCrc >> 8);
	}

	nMain = nBuffer >> 3;
	while (nMain--)
	{
		ulCrc ^= *(CRC32_t *)pb; // Warning, this only works on little-endian.
		ulCrc  = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8);

		ulCrc  = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8);
		ulCrc  = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8);

		ulCrc  = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8);
		ulCrc ^= *(CRC32_t *)(pb + 4); // Warning, this only works on little-endian.
		ulCrc  = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8);
		ulCrc  = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8);

		ulCrc  = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8);
		ulCrc  = pulCRCTable[(unsigned char)ulCrc] ^ (ulCrc >> 8);
		pb += 8;
	}

	nBuffer &= 7;
	goto JustAfew;
}
